package com.topsec.manager.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Properties;

/**
 * 
 * .
 * <p>
 * -----------------------------------------------------------------------------
 * <p>
 * 工程名 ： topsecweb
 * <p>
 * 
 * @author 赵艳生
 * @lastMonify 2018年10月30日
 * @since JDK1.8
 */
public class SocketUtil {
	/**ip地址*/
	private static String HOST = "";
	/**端口*/
	private static int PORT;
	/**计数器*/
	private static int count = 0;

	/**
	 * 获取连接
	 * 
	 * @param host
	 *            服务器地址
	 * @param port
	 *            端口
	 * @return Socket
	 */
	public static Socket getConnect(String host, int port) {
		Socket socket = null;
		try {
			socket = new Socket(HOST, PORT);

		} catch (Exception e) {
			e.printStackTrace();
		}
		return socket;
	}

	/**
	 * 关闭连接
	 * 
	 * @param socket
	 */
	public static void destroy(Socket socket) {
		try {
			socket.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 消息头部
	 * 
	 * @param type
	 *            消息类型 1:认证 2:保活 3:数据
	 * @param code
	 *            消息码 1:认证请求 2：IPjs数据 3：IPjs应答 4：认证应答报文 5：保活请求报文 6：保活应答报文
	 * @param length
	 *            消息长度
	 * @return byte
	 */
	public static byte[] getHeader(String type, String code, short length) {
		byte[] bytes = new byte[16];// 头部
		bytes[0] = 0x01;// version
		if ("1".equals(type)) {// Message Type
			bytes[1] = 0x01;
		} else if ("2".equals(type)) {
			bytes[1] = 0x02;
		} else if ("3".equals(type)) {
			bytes[1] = 0x03;
		}
		short nValue = 0x0101;// 消息码
		if ("1".equals(code)) {// Message Type
			nValue = 0x0101;
		} else if ("2".equals(code)) {
			nValue = (short) 0x9901;
		} else if ("3".equals(code)) {
			nValue = (short) 0x9902;
		} else if ("4".equals(code)) {
			nValue = (short) 0X0102;
		} else if ("5".equals(code)) {
			nValue = (short) 0X0201;
		} else if ("6".equals(code)) {
			nValue = (short) 0X0202;
		}
		byte[] lh = shortToBytes(nValue);// Message Code
		bytes[2] = lh[0];
		bytes[3] = lh[1];
		byte[] lh2 = toLH(count++);// Message Id
		bytes[4] = lh2[0];
		bytes[5] = lh2[1];
		bytes[6] = lh2[2];
		bytes[7] = lh2[3];
		if ("3".equals(type)) {
			length = 28;
		} else {
			length = 24;
		}
		byte[] shortToBytes4 = shortToBytes(length);
		bytes[8] = shortToBytes4[0];
		bytes[9] = shortToBytes4[1];
		short count = 1;// Data_Count
		byte[] shortToBytes = shortToBytes(count);
		bytes[10] = shortToBytes[0];
		bytes[11] = shortToBytes[1];
		short resv = 0;// Resv
		byte[] shortToBytes2 = shortToBytes(resv);
		bytes[12] = shortToBytes2[0];
		bytes[13] = shortToBytes2[1];
		short checksum = checkSum(bytes, bytes.length);
		byte[] shortToBytes3 = shortToBytes(checksum);
		bytes[14] = shortToBytes3[0];
		bytes[15] = shortToBytes3[1];
		return bytes;
	}

	/**
	 * 获取规则类别消息体
	 * 
	 * @param str
	 *            二进制字符串 1000100010100 13bit
	 *            规则标示从第1位开始分别为源IP,源IP掩码、目的IP、目的IP掩码、源端口
	 *            、源端口源码、目的端口、目的端口掩码、协议、协议掩码、 动作、IP4/6、持续。其中协议采用16位 存在是1否则是0
	 * @return byte[] 数据共占13bit 剩余4个bit为保留字节
	 * @return
	 */
	public static byte[] getBody_1(String str) {
		byte[] body = new byte[4];
		short parseShort = Short.parseShort(str, 2);
		byte[] shortToBytes = shortToBytes(parseShort);
		body[0] = shortToBytes[0];
		body[1] = shortToBytes[1];
		body[2] = (byte) 1;// 业务类型
		body[3] = (byte) 0;// 属下长度
		return body;
	}

	/**
	 * 获取规则内容消息体
	 * 
	 * @param type
	 *            1:源ip/目的ip 3:源ip+目的ip
	 * @param sip
	 *            源ip
	 * @param sport
	 *            源端口
	 * @param dip
	 *            目的ip
	 * @param dport
	 *            目的端口
	 * @param proto
	 *            协议
	 * @return byte[]
	 */
	public static byte[] getBody_2(String type, String sip, short sport,
			String dip, short dport, short proto) {
		byte[] body = null;
		if ("1".equals(type)) {
			body = new byte[8];
			String[] split1 = sip.split("\\.");
			int cmd = 0;
			body[cmd++] = (byte) Short.parseShort(split1[0]);
			body[cmd++] = (byte) Short.parseShort(split1[1]);
			body[cmd++] = (byte) Short.parseShort(split1[2]);
			body[cmd++] = (byte) Short.parseShort(split1[3]);
			byte[] shortToBytes = shortToBytes(sport);
			body[cmd++] = shortToBytes[0];
			body[cmd++] = shortToBytes[1];
			byte[] shortToBytes2 = shortToBytes(proto);
			body[cmd++] = shortToBytes2[0];
			body[cmd++] = shortToBytes2[1];
		} else if ("2".equals(type)) {
			// 源ip+目的ip
		}

		return body;
	}

	/**
	 * 获取属性消息体----待完善
	 *
	 * @return
	 */
	public static byte[] getBody_3() {
		byte[] body = new byte[28];
		int cmd = 0;
		int find = 1;// 发现信息
		byte[] lh = toLH(find);
		body[cmd++] = lh[0];
		body[cmd++] = lh[1];
		body[cmd++] = lh[2];
		body[cmd++] = lh[3];

		int check = 1;// 验证消息
		byte[] lh2 = toLH(check);
		body[cmd++] = lh2[0];
		body[cmd++] = lh2[1];
		body[cmd++] = lh2[2];
		body[cmd++] = lh2[3];

		short checkresult = 0;// 验证结果
		byte[] shortToBytes = shortToBytes(checkresult);
		body[cmd++] = shortToBytes[0];
		body[cmd++] = shortToBytes[1];

		short hc = 0;// 回传策略
		byte[] shortToBytes2 = shortToBytes(hc);
		body[cmd++] = shortToBytes2[0];
		body[cmd++] = shortToBytes2[1];

		short d = 0x0003;// 回传目的地
		byte[] shortToBytes3 = shortToBytes(d);
		body[cmd++] = shortToBytes3[0];
		body[cmd++] = shortToBytes3[1];

		int id = 11;// 配置id
		byte[] lh3 = toLH(id);
		body[cmd++] = lh3[0];
		body[cmd++] = lh3[1];
		body[cmd++] = lh3[2];
		body[cmd++] = lh3[3];

		int type = 0;// 工具类型
		byte[] lh4 = toLH(type);
		body[cmd++] = lh4[0];
		body[cmd++] = lh4[1];
		body[cmd++] = lh4[2];
		body[cmd++] = lh4[3];

		short bq = 0;// 标签
		byte[] shortToBytes31 = shortToBytes(bq);
		body[cmd++] = shortToBytes31[0];
		body[cmd++] = shortToBytes31[1];
		return body;
	}

	/**
	 * 校验和 fdfe 65022
	 * 
	 * @param buf
	 * @param length
	 * @return
	 */
	private static short checkSum(byte[] buf, int length) {
		short cksum = 0;
		int k = 0;
		while (length > 1) {
			if (k > (length / 2)) {
				break;
			} else {
				cksum += (char) ((buf[k * 2] << 8) + buf[k * 2 + 1]);
				length -= 2;
				k += 1;
			}

		}
		if (length > 0) {
			cksum += (char) (buf[k * 2] << 8);
		} else {
			cksum = (short) ((cksum >> 16) + (cksum & 0xffff));
			cksum += (cksum >> 16) + (cksum & 0xffff);
			String binaryString = Integer.toBinaryString(~cksum);
			cksum = Short.parseShort(binaryString);
		}
		return cksum;

	}

	/**
	 * @param 将int转为低字节在前
	 *            高字节在后的byte数组
	 * @return byte[]
	 */
	private static byte[] toLH(int n) {
		byte[] b = new byte[4];
		b[0] = (byte) (n & 0xff);
		b[1] = (byte) (n >> 8 & 0xff);
		b[2] = (byte) (n >> 16 & 0xff);
		b[3] = (byte) (n >> 24 & 0xff);
		return b;
	}

	/**
	 * @param 将float转为低字节在前
	 *            高字节在后的byte数组
	 * @return byte[]
	 */
	private static byte[] toLH(float f) {
		return toLH(Float.floatToRawIntBits(f));
	}

	/**
	 * 
	 * @param 将long转为低字节在前高字节在后的byte数组
	 * @return byte[]
	 */
	private static byte[] longToByte(long number) {
		long temp = number;
		byte[] b = new byte[8];
		for (int i = 0; i < b.length; i++) {
			b[i] = new Long(temp & 0xff).byteValue();//
			temp = temp >> 8; // 向右移8位
		}
		return b;
	}

	/**
	 * @param将short转为低字节在前高字节在后的byte数组
	 * @return byte[]
	 */
	private static byte[] shortToBytes(short n) {
		byte[] b = new byte[2];
		b[1] = (byte) (n & 0xff);
		b[0] = (byte) ((n >> 8) & 0xff);
		return b;
	}

	/**
	 * 测试方法
	 * 
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		try {
			Properties p = new Properties();
			File f = new File("conf.properties");
			if (!f.exists()) {
				f.createNewFile();
			}
			p.load(new FileInputStream("conf.properties"));
			HOST = (String) p.get("HOST");
			PORT = Integer.valueOf((String) p.get("PORT"));
			Socket socket = new Socket(HOST, PORT);
			PrintStream out = new PrintStream(socket.getOutputStream());
			// 认证==============================start
			byte[] bytes = getHeader("1", "1", (short) 24);
			byte[] body = new byte[8];// 消息内容----请求
			long b = 1;
			byte[] longToByte = longToByte(b);
			for (int i = 0; i < longToByte.length; i++) {
				body[i] = longToByte[i];
			}
			out.write(bytes);
			out.write(body);
			InputStream is = socket.getInputStream();
			byte[] reb = new byte[18];
			is.read(reb);
			System.out.println("服务器返回认证,长度[" + reb.length + "]消息内容[" + reb[0]
					+ reb[1] + reb[2] + reb[3] + reb[4] + "..." + reb[16]
					+ reb[17] + "]");
			// 认证==============================end
			Thread.sleep(1000);
			// 下发==============================start
			byte[] header = getHeader("3", "2", (short) 28);
			byte[] body1 = getBody_1("1000100010100");
			byte[] body2 = getBody_2("1", "10.172.43.19", (short) 3333, null,
					(short) 0, (short) 16);
			out.write(header);
			out.write(body1);
			out.write(body2);
			InputStream is1 = socket.getInputStream();
			byte[] res = new byte[18];
			is1.read(res);
			System.out.println("服务器返回下发结果,长度[" + reb.length + "]消息内容[" + res[0]
					+ res[1] + res[2] + res[3] + res[4] + "..." + res[16]
					+ res[17] + "]");
			// 下发==============================end
			socket.close();
			out.flush();
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("can not listen to:" + e);
		}
	}
}
